package com.ruben.utils;/**
 * @ClassName: FunctionUtils
 * @Date: 2020/11/9 0009 23:43
 * @Description:
 */

import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SerializedLambda;
import com.ruben.pojo.User;

import java.io.*;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;

/**
 * @ClassName: FunctionUtils
 * @Description: 我还没有写描述
 * @Date: 2020/11/9 0009 23:43
 * *
 * @author: <achao1441470436@gmail.com>
 * @version: 1.0
 * @since: JDK 1.8
 */
public class FunctionUtils {

    /**
     * @MethodName: toHashMap
     * @Description: list -> hashMap
     * @Date: 2021/1/31 0031 16:17
     * *
     * @author: <achao1441470436@gmail.com>
     * @param: [list, keyFunction, valueFunction]
     * @returnValue: java.util.Map<K, U>
     */
    public static <T, K, U> Map<K, U> toHashMap(List<T> list, Function<T, K> keyFunction, Function<T, U> valueFunction) {
        if (Objects.isNull(list) || list.isEmpty()) {
            return new HashMap<>(0);
        }
        return list.stream().collect(MyCollectors.toHashMap(keyFunction, valueFunction));
    }

    public static <T, K> Map<K, T> toHashMap(List<T> list, Function<T, K> keyFunction) {
        return toHashMap(list, keyFunction, null);
    }

    public static <T> Map<?, T> toHashMap(List<T> list) {
        return toHashMap(list, null, null);
    }

    public static void main(String[] args) {
        // IllegalStateException: Duplicate key
//        List<User> users = new ArrayList<>(Arrays.asList(new User(null, "HiNo"), new User(null, "SuPa")));
        // NullPointerException
        List<User> users = new ArrayList<>(Arrays.asList(new User("SuPa", null), new User("HiNo", null)));

//        Map<String, String> map = users.stream().collect(HashMap::new, (m, v) -> m.put(v.getUsername(), v.getPassword()), HashMap::putAll);

        Map<Integer, User> map = (Map<Integer, User>) toHashMap(users);
        map.forEach((k, v) -> System.out.println(k + "" + v));
    }

    /**
     * @ClassName: MyCollectors
     * @Date: 2020/12/4 0004 11:16
     * @Description: 我的自定义Collectors
     * @Author: <achao1441470436@gmail.com>
     */
    public static class MyCollectors {
        /**
         * @MethodName: toHashMap
         * @Description: 转换成hashmap
         * @Date: 2020/12/4 0004 13:39
         * *
         * @author: <achao1441470436@gmail.com>
         * @param: [methodName]
         * @returnValue: java.util.stream.Collector
         */
        public static <T, K, U> Collector<T, HashMap<K, U>, HashMap<K, U>> toHashMap(Function<T, K> keyFunction, Function<T, U> valueFunction) {
            return new MyCollectorImpl<>(HashMap::new, (m, v) -> m.put(Optional.ofNullable(keyFunction).map(keyF -> keyF.apply(v)).orElseGet(() -> {
                try {
                    return (K) v.getClass().getMethod("getId").invoke(v);
                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                    e.printStackTrace();
                }
                return null;
            }), Optional.ofNullable(valueFunction).map(valueF -> valueF.apply(v)).orElse((U) v)), (u, v) -> null, i -> i, Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH)));
        }

        public static class MyCollectorImpl<T, A, R> implements Collector<T, A, R> {
            private final Supplier<A> supplier;
            private final BiConsumer<A, T> accumulator;
            private final BinaryOperator<A> combiner;
            private final Function<A, R> finisher;
            private final Set<Characteristics> characteristics;

            MyCollectorImpl(Supplier<A> supplier,
                            BiConsumer<A, T> accumulator,
                            BinaryOperator<A> combiner,
                            Function<A, R> finisher,
                            Set<Characteristics> characteristics) {
                this.supplier = supplier;
                this.accumulator = accumulator;
                this.combiner = combiner;
                this.finisher = finisher;
                this.characteristics = characteristics;
            }

            @Override
            public Supplier<A> supplier() {
                return supplier;
            }

            @Override
            public BiConsumer<A, T> accumulator() {
                return accumulator;
            }

            @Override
            public BinaryOperator<A> combiner() {
                return combiner;
            }

            @Override
            public Function<A, R> finisher() {
                return finisher;
            }

            @Override
            public Set<Characteristics> characteristics() {
                return characteristics;
            }
        }
    }


    public static String getAttributeName(IFunction func) {
        if (!func.getClass().isSynthetic()) {
            throw ExceptionUtils.mpe("该方法仅能传入 lambda 表达式产生的合成类");
        }
        SerializedLambda lambda;
        try {
            ByteArrayOutputStream baos = new ByteArrayOutputStream(1024);
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(func);
            oos.flush();
            ObjectInputStream objIn = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())) {
                @Override
                protected Class<?> resolveClass(ObjectStreamClass objectStreamClass) throws IOException, ClassNotFoundException {
                    Class<?> clazz = super.resolveClass(objectStreamClass);
                    return clazz == java.lang.invoke.SerializedLambda.class ? SerializedLambda.class : clazz;
                }
            };
            lambda = (SerializedLambda) objIn.readObject();
        } catch (IOException | ClassNotFoundException e) {
            throw new RuntimeException("这不可能发生！", e);
        }
        String methodName = lambda.getImplMethodName();
        return methodName.substring(3, 4).toLowerCase() + methodName.substring(4);
    }


    @FunctionalInterface
    public interface IFunction<T, R> extends Serializable {
        R apply(T t);
    }

}
